import { watch, onUnmounted } from 'vue';
import { buildUUID } from '/@/utils/uuid';
import { useGlobSetting } from '/@/hooks/setting';
import { useUserStore } from '/@/store/modules/user';
import { isArray } from '/@/utils/is';
import { getToken } from '/@/utils/auth';
// vxe socket
const vs = {
    // 页面唯一 id，用于标识同一用户，不同页面的websocket
    pageId: buildUUID(),
    // webSocket 对象
    ws: null,
    // 一些常量
    constants: {
        // 消息类型
        TYPE: 'type',
        // 消息数据
        DATA: 'data',
        // 消息类型：心跳检测
        TYPE_HB: 'heart_beat',
        // 消息类型：更新vxe table数据
        TYPE_UVT: 'update_vxe_table',
    },
    // 心跳检测
    heartCheck: {
        // 间隔时间，间隔多久发送一次心跳消息
        interval: 10000,
        // 心跳消息超时时间，心跳消息多久没有回复后重连
        timeout: 6000,
        timeoutTimer: -1,
        clear() {
            clearTimeout(this.timeoutTimer);
            return this;
        },
        start() {
            vs.sendMessage(vs.constants.TYPE_HB, '');
            // 如果超过一定时间还没重置，说明后端主动断开了
            this.timeoutTimer = window.setTimeout(() => {
                vs.reconnect();
            }, this.timeout);
            return this;
        },
        // 心跳消息返回
        back() {
            this.clear();
            window.setTimeout(() => this.start(), this.interval);
        },
    },
    /** 初始化 WebSocket */
    initialWebSocket() {
        if (this.ws === null) {
            const userId = useUserStore().getUserInfo?.id;
            const domainURL = useGlobSetting().uploadUrl;
            const domain = domainURL.replace('https://', 'wss://').replace('http://', 'ws://');
            const url = `${domain}/vxeSocket/${userId}/${this.pageId}`;
            //update-begin-author:taoyan date:2022-4-24 for: v2.4.6 的 websocket 服务端，存在性能和安全问题。 #3278
            let token = (getToken() || '');
            this.ws = new WebSocket(url, [token]);
            //update-end-author:taoyan date:2022-4-24 for: v2.4.6 的 websocket 服务端，存在性能和安全问题。 #3278
            this.ws.onopen = this.on.open.bind(this);
            this.ws.onerror = this.on.error.bind(this);
            this.ws.onmessage = this.on.message.bind(this);
            this.ws.onclose = this.on.close.bind(this);
        }
    },
    // 发送消息
    sendMessage(type, message) {
        try {
            let ws = this.ws;
            if (ws != null && ws.readyState === ws.OPEN) {
                ws.send(JSON.stringify({
                    type: type,
                    data: message,
                }));
            }
        }
        catch (err) {
            console.warn('【JVxeWebSocket】发送消息失败：(' + err.code + ')');
        }
    },
    /** 绑定全局VXE表格 */
    tableMap: new Map(),
    /** 添加绑定 */
    addBind(map, key, value) {
        let binds = map.get(key);
        if (isArray(binds)) {
            binds.push(value);
        }
        else {
            map.set(key, [value]);
        }
    },
    /** 移除绑定 */
    removeBind(map, key, value) {
        let binds = map.get(key);
        if (isArray(binds)) {
            for (let i = 0; i < binds.length; i++) {
                let bind = binds[i];
                if (bind === value) {
                    binds.splice(i, 1);
                    break;
                }
            }
            if (binds.length === 0) {
                map.delete(key);
            }
        }
        else {
            map.delete(key);
        }
    },
    // 呼叫绑定的表单
    callBind(map, key, callback) {
        let binds = map.get(key);
        if (isArray(binds)) {
            binds.forEach(callback);
        }
    },
    lockReconnect: false,
    /** 尝试重连 */
    reconnect() {
        if (this.lockReconnect)
            return;
        this.lockReconnect = true;
        setTimeout(() => {
            if (this.ws && this.ws.close) {
                this.ws.close();
            }
            this.ws = null;
            console.info('【JVxeWebSocket】尝试重连...');
            this.initialWebSocket();
            this.lockReconnect = false;
        }, 5000);
    },
    on: {
        open() {
            console.info('【JVxeWebSocket】连接成功');
            this.heartCheck.start();
        },
        error(e) {
            console.warn('【JVxeWebSocket】连接发生错误:', e);
            this.reconnect();
        },
        message(e) {
            // 解析消息
            let json;
            try {
                json = JSON.parse(e.data);
            }
            catch (e) {
                console.warn('【JVxeWebSocket】收到无法解析的消息:', e.data);
                return;
            }
            let type = json[this.constants.TYPE];
            let data = json[this.constants.DATA];
            switch (type) {
                // 心跳检测
                case this.constants.TYPE_HB:
                    this.heartCheck.back();
                    break;
                // 更新form数据
                case this.constants.TYPE_UVT:
                    this.callBind(this.tableMap, data.socketKey, (args) => this.onVM.onUpdateTable(args, ...data.args));
                    break;
                default:
                    console.warn('【JVxeWebSocket】收到不识别的消息类型:' + type);
                    break;
            }
        },
        close(e) {
            console.info('【JVxeWebSocket】连接被关闭:', e);
            this.reconnect();
        },
    },
    onVM: {
        /** 收到更新表格的消息 */
        onUpdateTable({ props, data, methods }, row, caseId) {
            if (data.caseId !== caseId) {
                const tableRow = methods.getIfRowById(row.id).row;
                // 局部保更新数据
                if (tableRow) {
                    if (props.reloadEffect) {
                        data.reloadEffectRowKeysMap[row.id] = true;
                    }
                    Object.assign(tableRow, row, { id: tableRow.id });
                    methods.getXTable().reloadRow(tableRow);
                }
            }
        },
    },
};
export function useWebSocket(props, data, methods) {
    const args = { props, data, methods };
    watch(() => props.socketReload, (socketReload) => {
        if (socketReload) {
            vs.initialWebSocket();
            vs.addBind(vs.tableMap, props.socketKey, args);
        }
        else {
            vs.removeBind(vs.tableMap, props.socketKey, args);
        }
    }, { immediate: true });
    /** 发送socket消息更新行 */
    function socketSendUpdateRow(row) {
        vs.sendMessage(vs.constants.TYPE_UVT, {
            socketKey: props.socketKey,
            args: [row, data.caseId],
        });
    }
    onUnmounted(() => {
        vs.removeBind(vs.tableMap, props.socketKey, args);
    });
    return {
        socketSendUpdateRow,
    };
}
